import { readFileSync, writeFileSync } from "fs"
import { glob } from "glob"
import * as fse from "fs-extra"
import * as UglifyJS from "uglify-js"
const { execFile } = require('child_process');

export class ModifyFileOptions {
	path!: string
	expr!: string | RegExp
	isReg?: boolean = false
	/**
	 * 替换次数
	 * - -1表示无限次
	 */
	replaceTimes?: number = 1
	replace!: string
}

export class ReplaceFileOptions {
	fileGlob!: string
	replace!: string
}

export class WrapFileContentOptions {
	fileGlob!: string
	wrapContent!: string
}

export class UglifyJSOptions {
	fileGlob!: string
}

export class PNGQuantOptions {
	fileGlob!: string
}

export class TFileUtils {
	async modifyFile(options: ModifyFileOptions) {
		console.log(`modifyFile: ${options.path}`, options)
		let defaultOptions = new ModifyFileOptions()
		options.replaceTimes = options.replaceTimes ?? defaultOptions.replaceTimes!

		let ls = glob.sync(options.path)
		ls.forEach(filepath => {
			let content = readFileSync(filepath, "utf-8")
			let content0 = content
			for (let i = 0; i < options.replaceTimes!; i++) {
				if (options.isReg) {
					content = content.replace(`/${options.expr}/mg`, options.replace)
				} else {
					content = content.replace(options.expr, options.replace)
				}
			}
			writeFileSync(filepath, content, "utf-8")
		})
	}

	removeFolder(folderPath: string): Promise<void> {
		return fse.remove(folderPath)
	}

	removeFolderSync(folderPath: string) {
		fse.removeSync(folderPath)
	}

	async execAsyncOn(dir: string, call: () => Promise<any>) {
		let cwd = process.cwd()
		process.chdir(dir)
		try {
			await call()
		} catch (e) {
			throw e
		} finally {
			process.chdir(cwd)
		}
	}

	async replaceFile(options: ReplaceFileOptions) {
		console.log(`replaceFile: ${options.replace} -> ${options.fileGlob}`)

		let ls = glob.sync(options.fileGlob)
		ls.forEach(filepath => {
			console.log(`>>> replace file: ${options.replace} -> ${filepath}`)
			fse.copyFileSync(options.replace, filepath, fse.constants.COPYFILE_FICLONE)
		})
	}

	async wrapFileContent(options: WrapFileContentOptions) {
		console.log(`wrapFileContent: ${options.fileGlob}`)

		let replaceSymbol = "${rawcontent}"
		let wrapContent = options.wrapContent
		let lines = wrapContent.split(replaceSymbol)
		let ls = glob.sync(options.fileGlob)
		ls.forEach(filepath => {
			console.log(`>>> wrap file content: ${filepath}`)
			let content = fse.readFileSync(filepath, { encoding: "utf-8" })
			if (content.startsWith(lines[0]) && content.endsWith(lines[1])) {
				console.log("skip wrap")
				console.log(content)
			} else {
				content = wrapContent.replace(replaceSymbol, content)
				fse.writeFileSync(filepath, content)
			}
		})
	}

	async uglifyJS(options: UglifyJSOptions) {
		console.log(`uglifyJS: ${options.fileGlob}`)

		let ls = glob.sync(options.fileGlob)
		ls.forEach(filepath => {
			console.log(`>>> uglify file content: ${filepath}`)
			let content = fse.readFileSync(filepath, { encoding: "utf-8" })
			let result = UglifyJS.minify(content)
			if (result.error) {
				console.error(result.error)
			}
			if (result.warnings) {
				console.warn(result.warnings)
			}
			let miniContent = result.code
			fse.writeFileSync(filepath, miniContent, { encoding: "utf-8" })
		})
	}

	async pngQuant(options: PNGQuantOptions) {
		console.log(`minify images: ${options.fileGlob}`)

		let ls = glob.sync(options.fileGlob)
		const pngquant = require('pngquant-bin');
		let tasks = ls.map(filepath => {
			console.log(`>>> minifying file: ${filepath}`)
			return new Promise<void>((resolve, reject) => {
				execFile(pngquant, ["256", '-o', filepath, filepath, "--force"], (err: any) => {
					if (err) {
						console.log('Image minify failed, filepath:', filepath, 'reason:', err);
						reject(err)
					} else {
						console.log('Image minified, filepath:', filepath);
						resolve()
					}
				});
			})
		})
		await Promise.all(tasks)
	}

}

export const FileUtils = new TFileUtils()
